/*************************************************************************************
 * The MOSES project, model based software engineering of networked embedded systems.
 * 
 * Copyright (C) 2010-2013, University of Applied Sciences Wildau,
 * Telematics Research Group, http://www.tm.th-wildau.de/research-group
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses>.
 * 
 * Contributors:
 *     University of Applied Sciences Wildau - initial API and implementation
 * 
 * If you are interested in a commercial license please contact us via 
 * e-mail: ralf.vandenhouten@th-wildau.de.
 *  
 *************************************************************************************/
package de.thwildau.tm.moses.abnf.generator

import com.google.inject.Inject
import de.thwildau.tm.moses.abnf.abnf.Rule
import de.thwildau.tm.moses.abnf.util.EmptyTreeIterator
import de.thwildau.tm.moses.xabnf.xabnf.GeneratorModel
import de.thwildau.tm.moses.xabnf.xabnf.RuleBinding
import org.apache.log4j.Logger
import org.eclipse.emf.common.util.TreeIterator
import org.eclipse.emf.ecore.EObject
import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.xtext.naming.IQualifiedNameConverter
import org.eclipse.xtext.naming.QualifiedName

/**
 * NameResolver class to retrieve names of Java classes, fields and methods for specific
 * ABNF elements. 
 */
class NameResolver {
	
	static val LOG = Logger::getLogger(typeof(NameResolver))
	
	@Inject
	private IQualifiedNameConverter converter;
	
	/**
	 * Returns the XAbnf-Resource for the given ABNF-Resource. By convention the XAbnf-file must have
	 * the same name as the Abnf-file (e.g. 'content.abnf' will search for 'content.xabnf'). The XAbnf-file
	 * must be in the same directory. This method return null, if no XAbnf-Resource is found. 
	 */
	private def getXabnfResource(Resource abnfResource) {
		val fileName = abnfResource.URI.trimFileExtension.lastSegment
		val newFile = fileName + ModelUtil::XABNF_FILE_EXTENSION;
		val xabnfUri = abnfResource.URI.trimSegments(1).appendSegment(newFile)
		try {
			val xabnfResource = abnfResource.resourceSet.getResource(xabnfUri, true)
			if(xabnfResource == null) {
				LOG.info("Could not find XAbnf file for " + abnfResource.URI.devicePath + ". Load was successful.")
			}
			return xabnfResource
		} catch(Exception e) {
			LOG.info("Could not find XAbnf file for " + abnfResource.URI.devicePath + ". Load was failed.", e) 
		}
	}
	
	def getXabnfContent(Resource resource) {
		val xabnfResource = resource.xabnfResource
		if(xabnfResource != null)
			xabnfResource.allContents
		else
			new EmptyTreeIterator
	}
	
	/**
	 * Returns true, if the given resource is the ABNF-CoreRule file.
	 */
	def boolean isAbnfCoreResource(Resource resource) {
		var resourceName = resource.URI.lastSegment
		return resourceName.equals(ModelUtil::CORE_RULE_FILE)
	}
	
	/**
	 * Returns the name of the target package, where the generated classes are located.
	 * If there is an Xabnf-Resource for the given Abnf-Resource the 'package' attribute
	 * of the GeneratorModel is consulted. If no 'package' was specified in the Xabnf-file
	 * the package name is computed from the name of the resource.
	 */
	def String getPackageName(Resource resource) {
		var defaultPackageName = getDefaultPackageName(resource.xabnfContent)
		if(defaultPackageName != null)
			return defaultPackageName.toString
		
		var resourceName = resource.URI.lastSegment
		var packageName = resourceName.substring(0, resourceName.indexOf("."))
		packageName.replace("-", ".")
	}
	
	/**
	 * Returns the full-qualified name for the given ABNF rule. 
	 * The package name is computed from the resource (see getPackageName(Resource)).
	 */
	def QualifiedName getFqn(Rule rule) {
		val resource = rule.eResource
		getFqn(resource, rule.getClassName(resource.xabnfContent))
	}
	
	/**
	 * Returns the full-qualified name for the given ABNF resource and className.
	 * The package name is computed from the resource (see getPackageName(Resource)).
	 */
	def QualifiedName getFqn(Resource resource, String className) {
		converter.toQualifiedName(resource.packageName).append(className)
	}
	
	/**
	 * Returns the full-qualified name for the given packageName and the className
	 * 
	 * @param packageName - the name of the package
	 * @param className - the name of the class 
	 */
	def QualifiedName getFqn(String packageName, String className) {
		converter.toQualifiedName(packageName).append(className)
	}
	
	/**
	 * Returns the Java file for the given full-qualified name.
	 */
	def getJavaFilename(QualifiedName fqn) {
		fqn.toString("/") + ".java"
	}
	
	/**
	 * Return the Java class name for the given ABNF rule. This method searches for RuleBindings in the 'content'
	 * parameters and returns the 'targetRuleName' of a matching RuleBinding. If no RuleBinding match, the Java class
	 * name is the name of the rule. 
	 */
	private def String getClassName(Rule rule, TreeIterator<EObject> allContents) {
		var String className
		for(rb : allContents.filter(typeof(RuleBinding)).toIterable) {
			if(rb.rule.equals(rule)) {
				className = rb.targetRuleName
			}
		}
		if(className == null)
			className = rule.name
		if(className == null) {
			LOG.warn("Could not find a name for rule " + rule + ". Content size was " + allContents.toList.size)
		}
		
		return className
	}
	
	/**
	 * Returns the default package name of the GeneratorModel
	 */
	private def getDefaultPackageName(TreeIterator<EObject> allContents) {
		for(gm : allContents.filter(typeof(GeneratorModel)).toIterable) {
			if(gm.package != null) {
				return QualifiedName::create(gm.package) 
			} else
				return null
		}
	}
}